/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package abricots.game;

import abricots.MyLog;
import abricots.entity.Entity;
import abricots.entity.state.EntityState;
import abricots.net.common.GameMessage.EntityMessage;
import abricots.net.common.GameMessage.WorldDiffMessage;
import com.esotericsoftware.minlog.Log;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 *
 * @author charly
 */
public class WorldStateManager {

    private final MyLog log = new MyLog(getClass().getSimpleName(), Log.LEVEL_DEBUG);
    private GameEnv env;
    private SortedMap<Long, WorldState> frames;

    public WorldStateManager(GameEnv env) {
        this.env = env;
        frames = new TreeMap<Long, WorldState>();
    }

    public void addState(long gameTime, int entityId) {
        WorldState worldState = getWorldState(gameTime);
        if (worldState.getEntitiesId().contains(entityId)) {
            log.trace("Entity " + entityId + " already present in frame " + gameTime);
        } else {
            worldState.getEntitiesId().add(entityId);
        }
    }

    public void clearAllBefore(int entityId, long gameTime) {
        Iterator<Long> it = frames.keySet().iterator();
        while (it.hasNext()) {
            Long time = it.next();
            if (time < gameTime) {
                removeEntity(entityId, it, time);
                continue;
            }
            break;
        }
//        log.trace("States for entity " + entityId + " cleared before " + gameTime + ", " + frames.size() + " left");
    }

    private void removeEntity(int id, Iterator<Long> framesKeysIt, long frameTime) {
        WorldState state = frames.get(frameTime);
        state.getEntitiesId().remove((Integer) id);
        if (state.getEntitiesId().isEmpty()) {
            framesKeysIt.remove();
        }
    }

    public void clearAllAfter(int entityid, long gameTime) {
        Iterator<Long> it = frames.keySet().iterator();
        while (it.hasNext()) {
            Long time = it.next();
            if (time <= gameTime) {
                continue;
            }
            removeEntity(entityid, it, time);
        }
//        log.trace("States for entity " + entityid + " cleared after " + gameTime);
    }

    public WorldDiffMessage buildDiff(long fromTime, long toTime) {
        List<EntityMessage> newEntities = new ArrayList<EntityMessage>();
        List<EntityMessage> removedEntities = new ArrayList<EntityMessage>();
        List<EntityState> modifiedEntities = new ArrayList<EntityState>();

        WorldState oldWorldState = getWorldState(fromTime);
        WorldState newWorldState = getWorldState(toTime);
//        log.trace("Building diff from " + fromTime + " with state " + oldWorldState.toString());
//        log.trace("                to " + toTime + " with state " + newWorldState.toString());
        for (int id : oldWorldState.getEntitiesId()) {
            Entity oldEntity = env.getEntitiesManager().getEntity(id);
            if (oldEntity == null) {
                removedEntities.add(new EntityMessage(-1, id, -1));
                log.trace("  Entity " + id + " not found, addded to Removed list");
                continue;
            }
            EntityState oldState = oldEntity.getStateManager().getEntityState(fromTime);
            EntityState newState = oldEntity.getStateManager().getEntityState(toTime);
            if (oldState == null) {
                log.debug("Entity " + id + " has no state for " + fromTime + ", needed to simulate from");
                continue;
            }
            if (newState == null) {
                log.debug("Entity " + id + " has no state for " + toTime + ", needed to simulate to");
                continue;
            }
            if (!newState.equalsRegardlessOfTime(oldState)) {
                log.trace("  Entity " + id + " differs, adding to Modified list");
                modifiedEntities.add(newState);
            }
        }

        for (int id : newWorldState.getEntitiesId()) {
            if (!oldWorldState.getEntitiesId().contains(id)) {
                Entity entity = env.getEntitiesManager().getEntity(id);
                if (entity == null) {
                    log.debug("Entity " + id + " present in worldstate " + toTime + ", but unknown to entitymanager");
                    continue;
                }
                EntityState newState = entity.getStateManager().getEntityState(toTime);
                if (newState == null) {
                    log.debug("Entity " + id + " has no state for " + toTime + ", needed for simulation");
                    continue;
                }
                log.trace("  Entity " + id + " was not present at " + fromTime + ", adding to New list");
                newEntities.add(new EntityMessage(-1, id, entity.getEntityTypeId()));
                modifiedEntities.add(newState);
            }
        }
        WorldDiffMessage diffMessage = new WorldDiffMessage(fromTime, toTime, newEntities, removedEntities, modifiedEntities);
        return diffMessage;
    }

    private WorldState getWorldState(long gameTime) {
        if (frames.containsKey(gameTime)) {
            return frames.get(gameTime);
        }
        WorldState state = new WorldState();
        frames.put(gameTime, state);
        return state;
    }

    public long getLastStateUpTo(long gameTime) {
        if (frames.isEmpty()) {
            return -1;
        }
        log.trace("want last state up to " + gameTime + ", i have " + frames.keySet().toString());
        long last = -1;
        for (Long time : frames.keySet()) {
            if (time <= gameTime) {
                last = time;
                continue;
            }
            break;
        }
        return last;
    }

    public class WorldState {

        // present entities
        private final List<Integer> entitiesId;

        public WorldState() {
            entitiesId = new ArrayList<Integer>();
        }

        public List<Integer> getEntitiesId() {
            return entitiesId;
        }

        @Override
        public String toString() {
            return "WorldState with entities " + entitiesId.toString();
        }
    }
}
